package com.jintian.smart.kernel.flowable.flowable.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.jintian.smart.kernel.core.annotation.flow.FlowOperate;
import com.jintian.smart.kernel.core.annotation.flow.TaskOperate;
import com.jintian.smart.kernel.core.auth.IOperator;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.flowable.engine.BaseProcessService;
import com.jintian.smart.kernel.flowable.flowable.common.VariablesEnum;
import com.jintian.smart.kernel.flowable.flowable.service.ITaskQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
 * 流程任务查询
 */
@Component
public class TaskQueryHandler extends BaseProcessService implements ITaskQuery {
    protected static Logger logger = LoggerFactory.getLogger(TaskQueryHandler.class);

    @Override
    public Task taskId(String taskId) {
        return taskQuery().taskId(taskId).singleResult();
    }

    @Override
    public List<Task> taskCandidateUser(String candidateUser, int start, int limit) {
        return taskQuery().taskCandidateUser(candidateUser).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().listPage(start, limit);
    }

    @Override
    public List<Task> taskAssignee(String assignee, int start, int limit) {
        return taskQuery().taskAssignee(assignee).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().listPage(start, limit);
    }

    @Override
    public List<Task> taskCandidateOrAssigned(String userId) {
        return taskQuery().taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().list();
    }

    @Override
    public List<HistoricTaskInstance> taskAssigneeHistory(String assignee, int start, int limit) {
        return historicTaskInstanceQuery().taskAssignee(assignee).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().listPage(start, limit);
    }

    private TaskQuery buildTaskQueryByVariables(Map<String, Object> args) {
        TaskQuery tq = taskQuery();
        if (args != null && args.size() > 0) {
            for (Entry<String, Object> entry : args.entrySet()) {
                VariablesEnum var = VariablesEnum.of(entry.getKey());
                if (var != null && var.isShouldLike()) {
                    tq.processVariableValueLike(entry.getKey(), String.valueOf(entry.getValue()));
                } else {
                    tq.processVariableValueEquals(entry.getKey(), entry.getValue());
                }
            }
        }
        return tq;
    }

    @Override
    public List<Task> taskCandidateUserByCondition(String candidateUser, Map<String, Object> variables, int start, int limit) {
        return buildTaskQueryByVariables(variables).taskCandidateUser(candidateUser)
                .orderByTaskPriority().desc().orderByTaskCreateTime().asc().listPage(start, limit);
    }

    @Override
    public List<Task> taskAssigneeByCondition(String assignee, Map<String, Object> variables, int start, int limit) {
        return buildTaskQueryByVariables(variables).taskAssignee(assignee).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().listPage(start, limit);
    }

    @Override
    public List<Task> taskCandidateOrAssignedByCondition(String userId, Map<String, Object> variables, int start, int limit) {
        return buildTaskQueryByVariables(variables).taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().listPage(start, limit);
    }

    @Override
    public long countTaskCandidateUser(String candidateUser) {
        return taskQuery().taskCandidateUser(candidateUser).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public long countTaskAssignee(String assignee) {
        return taskQuery().taskAssignee(assignee).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public long countTaskCandidateOrAssigned(String userId) {
        return taskQuery().taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public long countTaskCandidateUserByCondition(String candidateUser, Map<String, Object> variables) {
        return buildTaskQueryByVariables(variables).taskCandidateUser(candidateUser).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public long countTaskAssigneeByCondition(String assignee, Map<String, Object> variables) {
        return buildTaskQueryByVariables(variables).taskAssignee(assignee).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public long countTaskCandidateOrAssignedByCondition(String userId, Map<String, Object> variables) {
        return taskQuery().taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                .orderByTaskCreateTime().asc().count();
    }

    @Override
    public String findBusinessKeyByTaskId(String taskId) {
        Task task = this.taskQuery().taskId(taskId).singleResult();
        ProcessInstance pi = runtimeService().createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).singleResult();
        if (pi != null) {
            return pi.getBusinessKey();
        }
        return null;
    }

    @Override
    public String findVariableByTaskId(String taskId, String variableName) {
        Object value = taskService().getVariable(taskId, variableName);
        return String.valueOf(value);
    }

    @Override
    public long countTaskAssigneeByTaskQuery(String assignee, TaskQuery query) {
        return query.taskAssignee(assignee).orderByTaskPriority().desc().orderByTaskCreateTime().asc().count();
    }

    @Override
    public List<Task> taskAssigneeByTaskQuery(String assignee, TaskQuery query, int start, int limit) {
        return query.taskAssignee(assignee).orderByTaskPriority().desc().orderByTaskCreateTime().asc()
                .listPage(start, limit);
    }

    @FlowOperate
    public TaskEntity getPrincipalTask(String processInstanceId) {
        IOperator checkIOperator = FlowAction.getFlowOwner();
        TaskEntity task = this.getPrincipalTask(processInstanceId, checkIOperator);
        return task;
    }

    @FlowOperate
    public List<TaskEntity> findPrincipalTaskList(String processInstanceId) {
        IOperator checkIOperator = FlowAction.getFlowOwner();
        return this.findPrincipalTaskList(processInstanceId, checkIOperator);
    }

    @TaskOperate
    public Task ownTask(String taskId) {
        TaskEntity task = this.loadTask(taskId);
        if (task == null) {
            return null;
        }
        if (this.validTask(task, FlowAction.getFlowOwner(), TaskCheckEnum.OwnerOrAssignee) == null) {
            return task;
        }
        return null;
    }
}